#include "defs.h"
#include "clr.h"

Clr_T::Clr_T(const char * nativeFilename)
	: _hostPtr(0)
	, _thunkPtr(0)
	, _defaultDomainPtr(0)
	, _setupPtr(0)
	, _filterDomainPtr(0)
	, _instancePtr(0)
{
	HRESULT hr;
	_ObjectHandle * handlePtr;
	VARIANT v;

	hr = CorBindToRuntimeEx(
		CLR_VERSION,
		CLR_FLAVOR,
		STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC,
		CLSID_CorRuntimeHost, 
		IID_ICorRuntimeHost, 
		(void **)&_hostPtr);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to instantiate CLR %S, %S", CLR_VERSION, CLR_FLAVOR);
	}

	hr = _hostPtr->Start();
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to start the CLR");
	}

	//
	// Get the default AppDomain created by the CLR
	//

	hr = _hostPtr->GetDefaultDomain(&_thunkPtr);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to get CLR default domain");
	}

	hr = _thunkPtr->QueryInterface(__uuidof(_AppDomain), (void**)&_defaultDomainPtr);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to get CLR default domain");
	}

	//
	// Create an instance of the object responsible for creating
	// the AppDomain to run the filters
	//

	hr = _defaultDomainPtr->CreateInstance(
		_bstr_t(MANAGED_ASSEMBLY),
		_bstr_t(MANAGED_SETUPCLASS),
		&handlePtr);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to create instance of '%S' in assembly '%S'", MANAGED_SETUPCLASS, MANAGED_ASSEMBLY);
	}

	VariantInit(&v);
	hr = handlePtr->Unwrap(&v);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to create instance of '%S' in assembly '%S'", MANAGED_SETUPCLASS, MANAGED_ASSEMBLY);
	}

	hr = v.pdispVal->QueryInterface(__uuidof(IProxySetup), (void**)&_setupPtr);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to create instance of '%S' in assembly '%S'", MANAGED_SETUPCLASS, MANAGED_ASSEMBLY);
	}

	//
	// Create the new AppDomain to run the filters
	//

	hr = _setupPtr->LoadDomain(_bstr_t(nativeFilename), &_filterDomainPtr);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to create AppDomain for managed filters");
	}

	//
	// Create the proxy instance, responsible for:
	// - collecting the configured managed filters
	// - enumerating the events the filters are interested in
	// - creating contexts
	// - delivering the events to the various filters
	// - shutting down
	//

	hr = _filterDomainPtr->CreateInstance(
		_bstr_t(MANAGED_ASSEMBLY),
		_bstr_t(MANAGED_INSTANCECLASS),
		&handlePtr);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to create instance of '%S' in assembly '%S'", MANAGED_INSTANCECLASS, MANAGED_ASSEMBLY);
	}

	VariantInit(&v);
	hr = handlePtr->Unwrap(&v);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to create instance of '%S' in assembly '%S'", MANAGED_INSTANCECLASS, MANAGED_ASSEMBLY);
	}

	hr = v.pdispVal->QueryInterface(__uuidof(IProxyInstance), (void**)&_instancePtr);
	if( FAILED(hr) )
	{
		throw Exception_T(hr, __FILE__, __LINE__, __FUNCSIG__,
			"Unable to create instance of '%S' in assembly '%S'", MANAGED_INSTANCECLASS, MANAGED_ASSEMBLY);
	}
}

Clr_T::~Clr_T(void)
{
	if( _instancePtr )
	{
		_instancePtr->Release();
		_instancePtr = 0;
	}

	if( _filterDomainPtr )
	{
		_filterDomainPtr->Release();
		_filterDomainPtr = 0;
	}

	if( _setupPtr )
	{
		_setupPtr->UnloadDomain();
		_setupPtr->Release();
		_setupPtr = 0;
	}

	if( _defaultDomainPtr )
	{
		_defaultDomainPtr->Release();
		_defaultDomainPtr = 0;
	}

	if( _thunkPtr )
	{
		_thunkPtr->Release();
		_thunkPtr = 0;
	}

	if( _hostPtr )
	{
		_hostPtr->Stop();
		_hostPtr->Release();
		_hostPtr = 0;
	}
}
